package cn.tristenstudy.blog.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * 敏感词过滤 工具类
 *
 * @author xichen1.li
 * @date 2021-01-19 17:43
 */
@Component
public class SensitiveWordUtil {

    private final static String WORDS = "WORDS";
    private final static String REPLACE_CHAR = "*";
    private static HashMap<Object, Object> sensitiveWordMap;
    /**
     * 最小匹配规则
     */
    private static final int MIN_MATCH_TYPE = 1;
    /**
     * 最大匹配规则
     */
    private static final int MAX_MATCH_TYPE = 2;

    private final Logger logger = LoggerFactory.getLogger(SensitiveWordUtil.class);
    /**
     * 敏感词缓存
     */
    @Autowired
    private SensitiveWordCache sensitiveWordCache;

    /**
     * 缓存敏感字符
     */
    @PostConstruct
    public void init() {
        List<String> list = readWordsFile();
        sensitiveWordCache.put(WORDS, list);
    }

    /**
     * 敏感词汇过滤替换为
     *
     * @param text 待检测文字
     * @return 替换后文字
     */
    public String replaceWords(String text) {
        if (StringUtils.isBlank(text)) {
            return text;
        }
        //缓存获取敏感词汇原记录
        List<String> words = sensitiveWordCache.get(WORDS);
        if (CollectionUtils.isEmpty(words)) {
            //读取敏感词汇文件，存入缓存
            words = readWordsFile();
            sensitiveWordCache.put(WORDS, words);
        }
        if (CollectionUtils.isEmpty(words)) {
            return text;
        }
        //屏蔽敏感词汇
        return replaceSensitiveWord(words, text);
    }

    /**
     * 替换敏感字字符
     *
     * @param data 敏感字集合
     * @param txt  待检查文本
     */
    private static String replaceSensitiveWord(List<String> data, String txt) {
        if (sensitiveWordMap == null) {
            addSensitiveWord(data);
        }
        String resultTxt = txt;
        //获取所有的敏感词
        List<String> set = getSensitiveWord(txt, MIN_MATCH_TYPE);
        for (String str : set) {
            resultTxt = resultTxt.replaceAll(str, REPLACE_CHAR);
        }
        return resultTxt;
    }

    /**
     * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型：
     * 说明：该方法来源于互联网
     */
    private static void addSensitiveWord(List<String> dataList) {
        sensitiveWordMap = new HashMap<>(dataList.size());
        Iterator<String> iterator = dataList.iterator();
        Map<Object, Object> now;
        Map<Object, Object> now2;
        while (iterator.hasNext()) {
            now2 = sensitiveWordMap;
            //敏感词
            String word = iterator.next().trim();
            for (int i = 0; i < word.length(); i++) {
                char keyWord = word.charAt(i);
                Object obj = now2.get(keyWord);
                //存在
                if (obj != null) {
                    now2 = (Map) obj;
                } else { //不存在
                    now = new HashMap<>();
                    now.put("isEnd", "0");
                    now2.put(keyWord, now);
                    now2 = now;
                }
                if (i == word.length() - 1) {
                    now2.put("isEnd", "1");
                }
            }
        }
    }

    /**
     * 获取内容中的敏感词
     * 说明：该方法来源于互联网
     *
     * @param text      内容
     * @param matchType 匹配规则 1=不最佳匹配，2=最佳匹配
     * @return
     */
    private static List<String> getSensitiveWord(String text, int matchType) {
        List<String> words = new ArrayList<String>();
        Map<Object, Object> now = sensitiveWordMap;
        //初始化敏感词长度
        int count = 0;
        //标志敏感词开始的下标
        int start = 0;
        for (int i = 0; i < text.length(); i++) {
            char key = text.charAt(i);
            now = (Map) now.get(key);
            //存在
            if (now != null) {
                count++;
                if (count == 1) {
                    start = i;
                }
                //敏感词结束
                if ("1".equals(now.get("isEnd"))) {
                    //重新获取敏感词库
                    now = sensitiveWordMap;
                    //取出敏感词，添加到集合
                    words.add(text.substring(start, start + count));
                    //初始化敏感词长度
                    count = 0;
                }
            } else {
                //不存在
                //重新获取敏感词库
                now = sensitiveWordMap;
                //不最佳匹配
                if (count == 1 && matchType == 1) {
                    count = 0;
                } else if (count == 1 && matchType == 2) {
                    //最佳匹配
                    words.add(text.substring(start, start + count));
                    count = 0;
                }
            }
        }
        return words;
    }

    /**
     * 读取敏感词汇文件
     */
    private List<String> readWordsFile() {
        List<String> list = new ArrayList<>();
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;
        try {
            Resource resource = new DefaultResourceLoader().getResource("classpath:CensorWords.txt");
            inputStream = resource.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
            bufferedReader = new BufferedReader(inputStreamReader);
            String txt = "";
            while (StringUtils.isNotBlank(txt = bufferedReader.readLine())) {
                list.addAll(
                        Arrays.asList(
                                StringUtils.split(
                                        StringUtils.deleteWhitespace(StringUtils.replace(txt, "，", ",")),
                                        ","
                                )
                        )
                );
            }
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
        } catch (Exception e) {
            logger.error("读取敏感词汇文件出错", e);
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (inputStreamReader != null) {
                    inputStreamReader.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                logger.error("读取敏感词汇文件出错", e);
            }
        }
        return list;
    }
}
